; 2 channels ADCPM sample player Z80 driver
; it can mix up to 2 ADCPM samples at a fixed 22050 Hz rate
; address and size of samples have to be 256 bytes boundary
; Stéphane Dallongeville @2011
;
; Z80 clock is 3.547 Mhz on PAL and 3.575 on NTSC

; we round to 161 cycles (pessimist case) per sample output which consist of :
; - read 2 ADCPM compressed samples from the rom, unpack and mix them in output buffer (121 cycles)
; - output the obtained sample to the DAC (30 cycles)
; - about 10 cycles for loop and synchro
;
; register usage :
; SP  = sample source (in ROM)
; HL  = mix buffer
; DE  = write buffer
; B  = $7F (used for mix overflow stuff) or $06 (used for adpcm decoding)
; C  = 00 (used for mix overflow stuff) or ?
; HL' = YMPORT0
; BC' = read buffer


; ###########################      define      ##############################

            INCLUDE "z80_def.i80"   ; basic definitions


; ###########################       var        ##############################

WAVADR      EQU     PARAMS+$00      ; WAV address (bit 8 --> bit 23)
WAVLEN      EQU     PARAMS+$02      ; WAV lenght (bit 8 --> bit 23)
IWAVADR     EQU     PARAMS+$10      ; internal WAV address (bit 8 --> bit 23)
IWAVLEN     EQU     PARAMS+$12      ; internal WAV lenght (bit 8 --> bit 23)

NULLWAVADR  EQU     PARAMS+$20      ; silent WAV adr
NULLWAVLEN  EQU     PARAMS+$22      ; silent WAV len

WRITEBUF    EQU     PARAMS+$24
READBUF     EQU     PARAMS+$26

SAVSMP      EQU     PARAMS+$28

TMPBUFFER   EQU     $1D00           ; tmp buffer for channel 1 decoding
WAVBUFFER0  EQU     $1E00           ; WAV buffer 0
WAVBUFFER1  EQU     $1F00           ; WAV buffer 1


; ###########################      macro       ##############################

            INCLUDE "z80_mac.i80"  ; basic macros


; copyChannelParams
; -----------------
; copy channel parameters to internal structure for channel 'ch'
; = 64 cycles

            macro copyChannelParams ch

            LD      HL, (WAVADR+(ch*4))     ;                   ' 16
            LD      (IWAVADR+(ch*4)), HL    ;                   ' 16
            LD      HL, (WAVLEN+(ch*4))     ;                   ' 16
            LD      (IWAVLEN+(ch*4)), HL    ;                   ' 16

            endm                            ;                   ' 64


; stopChannel
; -----------
; set null sample play for channel 'ch'
; = 131 cycles

            macro stopChannel ch

            LD      HL, (NULLWAVADR)        ;                   ' 16
            LD      (IWAVADR+(ch*4)), HL    ;                   ' 16
            LD      HL, (NULLWAVLEN)        ;                   ' 16
            LD      (IWAVLEN+(ch*4)), HL    ;                   ' 16
            clearPlayStatus ch              ; clear play status ' 25
            clearLoopStatus ch              ; clear loop status ' 25
            initSample ch                   ; init sample value ' 17

            endm                            ;                   ' 131


; saveSample
; ----------
; A    -> sample value
;
; save sample for channel 'ch'
; = 13 cycles

            macro saveSample ch

            LD      (SAVSMP+(ch)), A        ;                   ' 13

            endm                            ;                   ' 13


; restoreSample
; ----------
; A   <- sample value
;
; restore sample for channel 'ch'
; = 13 cycles

            macro restoreSample ch

            LD      A, (SAVSMP+(ch))        ;                   ' 13

            endm                            ;                   ' 13


; initSample
; ----------
; A   <- 0
;
; init sample for channel 'ch'
; = 17 cycles

            macro initSample ch

            XOR     A                       ;                   ' 4
            saveSample ch                   ;                   ' 13

            endm                            ;                   ' 17


; handlePlayCommandWhilePlay2
; ---------------------------
; HL  <-  ?
;
; handle play command for channel 'ch'
; = 233 cycles

            macro handlePlayCommandWhilePlay2 ch

            sampleOutput                ;                       ' 30 (30)

            getPlayCommand ch           ; play command ?        ' 22    | 32 (62)
            JP      NZ, .play           ;                       ' 10    |

            wait99                      ; synchro               ' 99    | 129 (191)
            sampleOutput                ; (1*161) (perfect)     ' 30    |

            wait15                      ;                       ' 15    | 25 (216)
            JP      .end                ;                       ' 10    |

.play
            clearPlayCommand ch         ; clear play command    ' 25    | 89 (151)
            copyChannelParams ch        ; copy params           ' 64    |

            wait10                      ; synchro               ' 10    | 40 (191)
            sampleOutput                ; (1*161) (perfect)     ' 30    |

            initSample ch               ; init sample value     ' 17 (208)
            setPlayStatus ch            ; set play status       ' 25 (233)

.end
            endm


; prepareChannelWhilePlay2
; ------------------------
; SP  <-  point to the sample source
; HL  <-  ?
; A   <-  ?
; E   <-  ?
;
; set bank and prepare registers to play channel 'ch'
; = 229 cycles

            macro prepareChannelWhilePlay2 ch

            sampleOutput                ;                       ' 30 (30)

            LD      HL, IWAVADR+(ch*4)  ; point to sample adr   ' 10    |
            LD      E, (HL)             ; E = bit 7-14          ' 7     |
            INC     L                   ;                       ' 4     | 125 (155)
            LD      A, (HL)             ; A = bit 15-22         ' 7     |
            setBankFast                 ; set bank              ' 97    |

            wait7                       ; synchro               ' 7     | 37 (192)
            sampleOutput                ; (1*161)+1 (bit late)  ' 30    |

            LD      A, E                ; A = bit 7-14          ' 4     |
            SCF                         ;                       ' 4     |
            RRA                         ; A = $80 | bit 8-14    ' 4     |
            LD      H, A                ;                       ' 4     | 37 (229)
            RRA                         ; A = bit 7 + ???       ' 4     |
            AND     $80                 ; A = bit 7 only        ' 7     |
            LD      L, A                ; L = bit 7 only        ' 4     |
            LD      SP, HL              ; SP = sample src       ' 6     |

            endm


; updateChannelData
; -----------------
; HL  <-  ?
;
; update sample address and sample lenght
; = 76 cycles

            macro updateChannelData ch

            LD      HL, (IWAVADR+(ch*4))    ; increment address ' 16    |
            INC     HL                      ;                   ' 6     | 38
            LD      (IWAVADR+(ch*4)), HL    ;                   ' 16    |

            LD      HL, (IWAVLEN+(ch*4))    ; decrement lenght  ' 16    |
            DEC     HL                      ;                   ' 6     | 38 (76)
            LD      (IWAVLEN+(ch*4)), HL    ;                   ' 16    |

            endm

; checkChannelEndWhilePlay2
; -------------------------
; HL  <-  ?
; A   <-  ?
;
; check is sample is done, restart it if needed
; = 322 cycles

            macro checkChannelEndWhilePlay2 ch

            sampleOutput                ;                       ' 30 (30)

            LD      HL, (IWAVLEN+(ch*4))    ; get sample lenght ' 16    |
            LD      A, H                    ;                   ' 4     | 34 (64)
            OR      L                       ;                   ' 4     |
            JP      Z, .done                ; sample done ?     ' 10    |

            wait97                      ; synchro               ' 97    | 127 (191)
            sampleOutput                ; (1*161) (perfect)     ' 30    |

            wait121                     ;                       ' 121   | 131 (322)
            JP      .end                ;                       ' 10    |

.done
            getLoopStatus ch            ; loop play ?           ' 22    | 32 (96)
            JP      Z, .stop            ;                       ' 10    |

.restart
            copyChannelParams ch        ; copy params           ' 64 (160)

            sampleOutput                ; (1*161)-1 (bit soon)  ' 30 (190)

            initSample ch               ; init sample value     ' 17    | 132 (322)
            wait105                     ;                       ' 105   |
            JP      .end                ;                       ' 10    |

.stop
            wait65                      ; synchro               ' 65    | 95 (191)
            sampleOutput                ; (1*161) (perfect)     ' 30    |

            stopChannel ch              ;                       ' 131 (322)

.end
            endm


; swapBuffer
; ----------
; HL  <-  point to new write buffer
; DE  <-  point to new read buffer
;
; switch read and write buffer
; = 72 cycles

            macro swapBuffer

            LD      HL, (WRITEBUF)      ;                       ' 16
            LD      DE, (READBUF)       ;                       ' 20
            LD      (WRITEBUF), DE      ;                       ' 20
            LD      (READBUF), HL       ;                       ' 16

            endm                        ;                       ' 72


; sampleOutput
; ------------
; HL'  -> YMPORT1
; BC' <-> point to play output buffer
; D    -> $80
;
; output a sample to the DAC
; = 30 cycles

            macro sampleOutput

            EXX                     ;                           ' 4
            LD      A, (BC)         ; read sample from buffer   ' 7
            ADD     D               ; unsign                    ' 4
            INC     C               ; increment with looping    ' 4
            LD      (HL), A         ; play sample               ' 7
            EXX                     ;                           ' 4


            endm                    ;                           ' 30


; protectedSampleOutput
; = 38 cycles

            macro protectedSampleOutput

            EX      AF, AF'         ; save A                    ' 4     |
            sampleOutput            ; sample output             ' 30    | 38
            EX      AF, AF'         ; restore A                 ' 4     |

            endm

; getFirstOffset
; = 19 cycles

            macro getFirstOffset

            LD      C, H                ; save H samples        ' 4     |
            LD      H, B                ;                       ' 4     | 19
            ADD     HL, HL              ; HL point to delta val ' 11    |

            endm

; getSecondOffset
; = 19 cycles

            macro getSecondOffset

            LD      L, C                ; get back H samples    ' 4     |
            LD      H, B                ;                       ' 4     | 19
            ADD     HL, HL              ; HL point to delta val ' 11    |

            endm

; decodeAndWriteSample
; = 22 cycles

            macro decodeAndWriteSample

            ADD     (HL)            ; decode                    ' 7     |
            INC     L               ; next delta                ' 4     | 22
            LD      (DE), A         ; write sample in buffer    ' 7     |
            INC     E               ;                           ' 4     |

            endm

; decodeAndWriteSample2
; = 44 cycles

            macro decodeAndWriteSample2

            decodeAndWriteSample    ;                           ' 22    | 44
            decodeAndWriteSample    ;                           ' 22    |

            endm


; doMix
; = 28 --> 36 cycles

            macro doMix

            ADD     (HL)            ; mix                       ' 7     | 17
            JP      PO, .ok         ; check overflow            ' 10    |

            LD      A, B            ; fix overflow              ' 4     | +8
            ADC     C               ; A = $7F/$80               ' 4     |

.ok
            LD      (HL), A         ; write sample in buffer    ' 7     | 11 (28)
            INC     L               ; next                      ' 4     |

            endm

; mixFirst
; = 32 --> 40 cycles

            macro mixFirst

            LD      A, E            ; first sample              ' 4     | 32
            doMix                   ; mix                       ' 28    |

            endm

; mixSecond
; = 32 --> 40 cycles

            macro mixSecond

            LD      A, D            ; second sample             ' 4     | 32
            doMix                   ; mix                       ' 28    |

            endm

; mix2
; = 64 --> 80 cycles

            macro mix2

            mixFirst                ;                           ' 32    | 64
            mixSecond               ;                           ' 32    |

            endm

; read
; = 10 cycles

            macro read

            POP     DE              ; read 2 samples from RAM   ' 10

            endm

; readAndMix2
; = 74 --> 90 cycles

            macro readAndMix2

            read                    ;                           ' 10    | 74
            mix2                    ;                           ' 64    |

            endm


; decode16WhilePlay5
; ------------------
; SP  <-> point to the sample source (ROM)
; DE  <-> point to the write output buffer
; HL  <-  ?
; A'  <-  current sample level
; A   <-  ?
; B    -> DELTATAB >> 1
; C   <-  ?
;
; read 16 adpcm samples and write them in output buffer
; while playing 5 samples
; = 746 cycles

            macro decode16WhilePlay5

            sampleOutput            ; sample output             ' 30    | 34
            EX      AF, AF'         ; get current level         ' 4     |

            ; 1 --------------------
            POP     HL              ; read 4 ADPCM samples      ' 10+2 (46)

            getFirstOffset          ;                           ' 19 (65)
            decodeAndWriteSample2   ;                           ' 44 (109)
            getSecondOffset         ;                           ' 19 (128)
            decodeAndWriteSample    ;                           ' 22 (150)

            ADD     (HL)            ; decode                    ' 7 (157)

            protectedSampleOutput   ; --> (1*161) (perfect)     ' 38 (195)

            INC     L               ; next delta                ' 4     | 15 (210)
            LD      (DE), A         ; write sample in buffer    ' 7     |
            INC     E               ;                           ' 4     |

            ; 2 --------------------
            POP     HL              ; read 4 ADPCM samples      ' 10+2  (222)

            getFirstOffset          ;                           ' 19 (241)
            decodeAndWriteSample2   ;                           ' 44 (285)
            getSecondOffset         ;                           ' 19 (304)

            ADD     (HL)            ; decode                    ' 7     |
            INC     L               ; next delta                ' 4     | 11 (315)

            NOP                     ; synchro                   ' 4 (319)
            protectedSampleOutput   ; --> (2*161)+1 (bit late)  ' 38 (357)

            LD      (DE), A         ; write sample in buffer    ' 7     | 11 (368)
            INC     E               ;                           ' 4     |

            decodeAndWriteSample    ;                           ' 22 (390)

            ; 3 --------------------
            POP     HL              ; read 4 ADPCM samples      ' 10+2 (402)

            getFirstOffset          ;                           ' 19 (421)
            decodeAndWriteSample2   ;                           ' 44 (465)
            getSecondOffset         ;                           ' 19 (484)

            protectedSampleOutput   ; --> (3*161)+5 (late !)    ' 38 (522)

            decodeAndWriteSample2   ;                           ' 44 (566)

            ; 4 --------------------
            POP     HL              ; read 4 ADPCM samples      ' 10+2 (578)

            getFirstOffset          ;                           ' 19 (597)
            decodeAndWriteSample2   ;                           ' 44 (641)

            protectedSampleOutput   ; --> (4*161)+1 (bit late)  ' 38 (679)

            getSecondOffset         ;                           ' 19 (698)
            decodeAndWriteSample2   ;                           ' 44 (742)

            EX      AF, AF'         ; save current level        ' 4  (746)

            endm                    ;                           ' 746


; decode20WhilePlay6
; ------------------
; SP  <-> point to the sample source (ROM)
; DE  <-> point to the write output buffer
; HL  <-  ?
; A'  <-  current sample level
; A   <-  ?
; B    -> DELTATAB >> 1
; C   <-  ?
;
; read 20 adpcm samples and write them in output buffer
; while playing 6 samples
; = 926 cycles

            macro decode20WhilePlay6

            sampleOutput            ; sample output             ' 30    | 34
            EX      AF, AF'         ; get current level         ' 4     |

            ; 1 --------------------
            POP     HL              ; read 4 ADPCM samples      ' 10+2 (46)

            getFirstOffset          ;                           ' 19 (65)
            decodeAndWriteSample2   ;                           ' 44 (109)
            getSecondOffset         ;                           ' 19 (128)
            decodeAndWriteSample    ;                           ' 22 (150)

            ADD     (HL)            ; decode                    ' 7 (157)

            protectedSampleOutput   ; --> (1*161) (perfect)     ' 38 (195)

            INC     L               ; next delta                ' 4     | 15 (210)
            LD      (DE), A         ; write sample in buffer    ' 7     |
            INC     E               ;                           ' 4     |

            ; 2 --------------------
            POP     HL              ; read 4 ADPCM samples      ' 10+2  (222)

            getFirstOffset          ;                           ' 19 (241)
            decodeAndWriteSample2   ;                           ' 44 (285)
            getSecondOffset         ;                           ' 19 (304)

            ADD     (HL)            ; decode                    ' 7     |
            INC     L               ; next delta                ' 4     | 11 (315)

            NOP                     ; synchro                   ' 4 (319)
            protectedSampleOutput   ; --> (2*161)+1 (bit late)  ' 38 (357)

            LD      (DE), A         ; write sample in buffer    ' 7     | 11 (368)
            INC     E               ;                           ' 4     |

            decodeAndWriteSample    ;                           ' 22 (390)

            ; 3 --------------------
            POP     HL              ; read 4 ADPCM samples      ' 10+2 (402)

            getFirstOffset          ;                           ' 19 (421)
            decodeAndWriteSample2   ;                           ' 44 (465)
            getSecondOffset         ;                           ' 19 (484)

            protectedSampleOutput   ; --> (3*161)+5 (late !)    ' 38 (522)

            decodeAndWriteSample2   ;                           ' 44 (566)

            ; 4 --------------------
            POP     HL              ; read 4 ADPCM samples      ' 10+2 (578)

            getFirstOffset          ;                           ' 19 (597)
            decodeAndWriteSample2   ;                           ' 44 (641)

            protectedSampleOutput   ; --> (4*161)+1 (bit late)  ' 38 (679)

            getSecondOffset         ;                           ' 19 (698)
            decodeAndWriteSample2   ;                           ' 44 (742)

            ; 5 --------------------
            POP     HL              ; read 4 ADPCM samples      ' 10+2  (754)

            getFirstOffset          ;                           ' 19 (773)
            decodeAndWriteSample    ;                           ' 22 (795)

            ADD     (HL)            ; decode                    ' 7 (802)

            protectedSampleOutput   ; --> (5*162)+1 (bit late)  ' 38 (840)

            INC     L               ; next delta                ' 4     |
            LD      (DE), A         ; write sample in buffer    ' 7     | 19 (859)
            INC     E               ;                           ' 4     |

            getSecondOffset         ;                           ' 19 (878)
            decodeAndWriteSample2   ;                           ' 44 (922)

            EX      AF, AF'         ; save current level        ' 4 (926)

            endm                    ;                           ' 926


; mix6WhilePlay2
; --------------
; SP  <-> point to the sample source
; HL  <-> point to write output buffer
; BC   -> BC = $7F00
; DE  <-  ?
; A   <-  ?
;
; mix 6 samples and mix them with output buffer
; = 290 --> 338 cycles

            macro mix6WhilePlay2

            sampleOutput            ;                           ' 30

            ; 1 --------------------
            readAndMix2             ;                           ' 74 (104)

            ; 2 --------------------
            read                    ;                           ' 10    | 42 (146)
            mixFirst                ;                           ' 32    |

            LD      A, D            ; get second sample         ' 4     | 11 (157)
            ADD     (HL)            ; mix                       ' 7     |

            protectedSampleOutput   ; --> (1*161) (perfect)     ' 38 (195)

            JP      PO, .ok         ; check overflow            ' 10 (205)

            LD      A, B            ; fix overflow              ' 4     | +8
            ADC     C               ; A = $7F/$80               ' 4     |

.ok
            LD      (HL), A         ; write sample in buffer    ' 7     | 11 (216)
            INC     L               ; next                      ' 4     |

            ; 3 --------------------
            readAndMix2             ;                           ' 74 (290)

            endm


; mix10WhilePlay3
; ---------------
; SP  <-> point to the sample source
; HL  <-> point to write output buffer
; BC   -> BC = $7F00
; DE  <-  ?
; A   <-  ?
;
; mix 10 samples and mix them with output buffer
; = 476 --> 556 cycles

            macro mix10WhilePlay3

            mix6WhilePlay2          ;                           ' 290

            ; 4 --------------------
            read                    ;                           ' 10 (300)

            LD      A, E            ; get first sample          ' 4     |
            ADD     (HL)            ; mix                       ' 7     | 21 (321)
            JP      PO, .ok         ; check overflow            ' 10    |

            LD      A, B            ; fix overflow              ' 4     | +8
            ADC     C               ; A = $7F/$80               ' 4     |

.ok
            protectedSampleOutput   ; --> (2*161)+3 (late !)    ' 38 (359)

            LD      (HL), A         ; write sample in buffer    ' 7     | 11 (370)
            INC     L               ; next                      ' 4     |

            mixSecond               ;                           ' 32 (402)

            ; 5 --------------------
            readAndMix2             ;                           ' 74 (476)

            endm


; mix20WhilePlay6
; ---------------
; SP  <-> point to the sample source
; HL  <-> point to write output buffer
; BC   -> BC = $7F00
; DE  <-  ?
; A   <-  ?
;
; mix 20 samples and mix them with output buffer
; = 944 --> 1104 cycles

            macro mix20WhilePlay6

            mix10WhilePlay3         ;                           ' 476 --> 556

            ; 6 --------------------
            read                    ;                           ' 10 (486)

            sampleOutput            ; --> (3*161)+3 (late !)    ' 30 (516)

            mixFirst                ;                           ' 32    | 64 (580)
            mixSecond               ;                           ' 32    |

            ; 7 --------------------
            read                    ;                           ' 10    | 42 (622)
            mixFirst                ;                           ' 32    |

            LD      A, D            ; get second sample         ' 4     |
            ADD     (HL)            ; mix                       ' 7     | 21 (643)
            JP      PO, .ok         ; check overflow            ' 10    |

            LD      A, B            ; fix overflow              ' 4     | +8
            ADC     C               ; A = $7F/$80               ' 4     |

.ok
            protectedSampleOutput   ; --> (4*161)+3 (late !)    ' 38 (681)

            LD      (HL), A         ; write sample in buffer    ' 7     | 11 (692)
            INC     L               ; next                      ' 4     |

            ; 8 --------------------
            readAndMix2             ;                           ' 74 (766)

            ; 9 --------------------
            read                    ;                           ' 10    | 42 (808)
            mixFirst                ;                           ' 32    |

            sampleOutput            ; --> (5*161)+3 (late !)    ' 30 (838)

            mixSecond               ;                           ' 32 (870)

            ; 10 --------------------
            readAndMix2             ;                           ' 74 (944)

            endm


; ###########################       init       ##############################

            ORG     $0000

init
            DI                      ; disable ints
            LD      SP, $1D00       ; setup stack
            IM      $01             ; set int mode 1
            XOR     A
            LD      (STATUS), A     ; driver not ready
            LD      (COMMAND), A    ; command cleared
            JP      start           ; jump to start


; ###########################     interrupt    ##############################

            BLOCK   $0038-$

interrupt                           ; do nothing in this program
            RETI


; ###########################       main       ##############################

            BLOCK   $0200-$

start
            LD      HL, PARAMS
            LD      A, $00
            LD      B, $20

cp_loop
            LD      (HL), A         ; clear sample parameters
            INC     HL
            DJNZ    cp_loop

            LD      HL, WAVBUFFER0
            LD      A, $00
            LD      B, $00          ; for 256 * 2 bytes to clear

cb_loop
            LD      (HL), A         ; initialise buffers to silent
            INC     HL
            LD      (HL), A
            INC     HL
            DJNZ    cb_loop

            CALL    initDAC         ; prepare DAC for output

            LD      HL, WAVBUFFER0  ; initialise write and read buffer
            LD      (WRITEBUF), HL
            LD      HL, WAVBUFFER1
            LD      (READBUF), HL

            LD      BC, HL          ; BC' point to read buffer
            LD      D, $80          ; D' = $80 (fast unsign)
            LD      HL, YMPORT1     ; HL' point to YMPORT1
            EXX

wait_nullwav
            LD      HL, (NULLWAVADR)
            LD      A, H
            OR      L
            JP      Z, wait_nullwav ; wait for null wav address being initialised
            LD      HL, (NULLWAVLEN)
            LD      A, H
            OR      L
            JP      Z, wait_nullwav ; wait for null wav len being initialised

            stopChannel 0           ; stop all channels
            stopChannel 1

            LD      B, $06          ; DELTATAB >> 9

            LD      A, STATREADY
            LD      (STATUS), A     ; driver ready

main_loop

; channel 0
; ---------

; $00-$01
            handlePlayCommandWhilePlay2 0   ;                   ' 233   | (2*161) = 322
            wait89                          ;                   ' 89    |

; $02-$03
            prepareChannelWhilePlay2 0  ; SP point to src       ' 229   |
            wait49                      ;                       ' 49    | (2*161) = 322
            LD      DE, (WRITEBUF)      ; DE point to write buf ' 16    |
            LD      IXL, 12             ; prepare loop counter  ' 11    |
            restoreSample 0             ; restore current level ' 13    |
            EX      AF, AF'             ; A' = current level    ' 4     |

; $04-$4B
loop_ch0
            decode20WhilePlay6          ; decode                ' 926   |
            wait22                      ; SP = src, DE = dst    ' 22    | (6*161) = 966
            DEC     IXL                 ;                       ' 8     |
            JP      NZ, loop_ch0        ;                       ' 10    |

; $4C-$50
            decode16WhilePlay5          ;                       ' 746   | (5*161) = 805
            EX      AF, AF'             ; A = current level     ' 4     |
            saveSample 0                ; save current level    ' 13    |
            wait42                      ;                       ' 42    |

; $51
            sampleOutput                ;                       ' 30    |
            updateChannelData 0         ; update channel data   ' 76    | 161
            wait55                      ;                       ' 55    |

; $52-$53
            checkChannelEndWhilePlay2 0 ;                       ' 322   | (2*161) = 322

; channel 1
; ---------

; $54-$55
            handlePlayCommandWhilePlay2 1   ;                   ' 233   | (2*161) = 322
            wait89                          ;                   ' 89    |

; $56-$57
            prepareChannelWhilePlay2 1  ; SP point to src       ' 229   |
            wait55                      ;                       ' 55    | (2*161) = 322
            LD      DE, TMPBUFFER       ; DE point to temp buf  ' 10    |
            LD      IXL, 12             ; prepare loop counter  ' 11    |
            restoreSample 1             ; restore current level ' 13    |
            EX      AF, AF'             ; A' = current level    ' 4     |

; $58-$9F
loop_ch1
            decode20WhilePlay6          ; decode                ' 926   |
            wait22                      ; SP = src, DE = dst    ' 22    | (6*161) = 966
            DEC     IXL                 ;                       ' 8     |
            JP      NZ, loop_ch1        ;                       ' 10    |

; $A0-$A4
            decode16WhilePlay5          ;                       ' 746   | (5*161) = 805
            EX      AF, AF'             ; A = current level     ' 4     |
            saveSample 1                ; save current level    ' 13    |
            wait42                      ;                       ' 42    |

; $A5
            sampleOutput                ;                       ' 30    |
            updateChannelData 1         ; update channel data   ' 76    | 161
            wait55                      ;                       ' 55    |

; $A6-$A7
            checkChannelEndWhilePlay2 1 ;                       ' 322   | (2*161) = 322

; mix time
; --------

; $A8
            sampleOutput                ;                       ' 30    |
            wait84                      ;                       ' 84    |
            LD      SP, TMPBUFFER       ; SP = sample src       ' 10    | 161
            LD      HL, (WRITEBUF)      ; HL = output buffer    ' 16    |
            LD      BC, $7F00           ; help for fast mix     ' 10    |
            LD      IXL, 12             ; init loop             ' 11    |

; $A9-$F0
loop_mix
            mix20WhilePlay6             ;                       ' 944   |
            wait4                       ;                       ' 4     | (6*161) = 966
            DEC     IXL                 ;                       ' 8     |
            JP      NZ, loop_mix        ;                       ' 10    |


; $F1-F3
            mix10WhilePlay3             ;                       ' 476   | (3*161) = 483
            wait7                       ;                       ' 7     |

; $F4-F5
            mix6WhilePlay2              ;                       ' 290   | (2*161) = 322
            wait21                      ;                       ' 21    |
            LD      IXL, 9              ; init free time loop   ' 11    |

; free time here
; --------------
loop_free
; $F6-$FE

            sampleOutput                ;                       ' 30    | 161
            wait131                     ;                       ' 113   |
            DEC     IXL                 ;                       ' 8     |
            JP      NZ, loop_free       ;                       ' 10    |

; $FF
            sampleOutput                ;                       ' 30    |
            swapBuffer                  ; swap buffers          ' 72    |
            EXX                         ;                       ' 4     |
            LD      BC, (READBUF)       ; read buffer           ' 20    | 161
            EXX                         ;                       ' 4     |
            LD      B, $06              ; DELTATAB >> 9         ' 7     |
            wait14                      ;                       ' 14    |
            JP      main_loop           ;                       ' 10    |


; ##############################  functions  ################################

            INCLUDE "z80_fct.i80"   ; basic functions


; ##############################    tables   ################################

            BLOCK   $0C00-$

DELTATAB
DELTATAB_0  DB      -34, -34, -21, -34, -13, -34, -8, -34, -5, -34, -3, -34, -2, -34, -1, -34
            DB      0, -34, 1, -34, 2, -34, 3, -34, 5, -34, 8, -34, 13, -34, 21, -34
DELTATAB_1  DB      -34, -21, -21, -21, -13, -21, -8, -21, -5, -21, -3, -21, -2, -21, -1, -21
            DB      0, -21, 1, -21, 2, -21, 3, -21, 5, -21, 8, -21, 13, -21, 21, -21
DELTATAB_2  DB      -34, -13, -21, -13, -13, -13, -8, -13, -5, -13, -3, -13, -2, -13, -1, -13
            DB      0, -13, 1, -13, 2, -13, 3, -13, 5, -13, 8, -13, 13, -13, 21, -13
DELTATAB_3  DB      -34, -8, -21, -8, -13, -8, -8, -8, -5, -8, -3, -8, -2, -8, -1, -8
            DB      0, -8, 1, -8, 2, -8, 3, -8, 5, -8, 8, -8, 13, -8, 21, -8
DELTATAB_4  DB      -34, -5, -21, -5, -13, -5, -8, -5, -5, -5, -3, -5, -2, -5, -1, -5
            DB      0, -5, 1, -5, 2, -5, 3, -5, 5, -5, 8, -5, 13, -5, 21, -5
DELTATAB_5  DB      -34, -3, -21, -3, -13, -3, -8, -3, -5, -3, -3, -3, -2, -3, -1, -3
            DB      0, -3, 1, -3, 2, -3, 3, -3, 5, -3, 8, -3, 13, -3, 21, -3
DELTATAB_6  DB      -34, -2, -21, -2, -13, -2, -8, -2, -5, -2, -3, -2, -2, -2, -1, -2
            DB      0, -2, 1, -2, 2, -2, 3, -2, 5, -2, 8, -2, 13, -2, 21, -2
DELTATAB_7  DB      -34, -1, -21, -1, -13, -1, -8, -1, -5, -1, -3, -1, -2, -1, -1, -1
            DB      0, -1, 1, -1, 2, -1, 3, -1, 5, -1, 8, -1, 13, -1, 21, -1
DELTATAB_8  DB      -34, 0, -21, 0, -13, 0, -8, 0, -5, 0, -3, 0, -2, 0, -1, 0
            DB      0, 0, 1, 0, 2, 0, 3, 0, 5, 0, 8, 0, 13, 0, 21, 0
DELTATAB_9  DB      -34, 1, -21, 1, -13, 1, -8, 1, -5, 1, -3, 1, -2, 1, -1, 1
            DB      0, 1, 1, 1, 2, 1, 3, 1, 5, 1, 8, 1, 13, 1, 21, 1
DELTATAB_A  DB      -34, 2, -21, 2, -13, 2, -8, 2, -5, 2, -3, 2, -2, 2, -1, 2
            DB      0, 2, 1, 2, 2, 2, 3, 2, 5, 2, 8, 2, 13, 2, 21, 2
DELTATAB_B  DB      -34, 3, -21, 3, -13, 3, -8, 3, -5, 3, -3, 3, -2, 3, -1, 3
            DB      0, 3, 1, 3, 2, 3, 3, 3, 5, 3, 8, 3, 13, 3, 21, 3
DELTATAB_C  DB      -34, 5, -21, 5, -13, 5, -8, 5, -5, 5, -3, 5, -2, 5, -1, 5
            DB      0, 5, 1, 5, 2, 5, 3, 5, 5, 5, 8, 5, 13, 5, 21, 5
DELTATAB_D  DB      -34, 8, -21, 8, -13, 8, -8, 8, -5, 8, -3, 8, -2, 8, -1, 8
            DB      0, 8, 1, 8, 2, 8, 3, 8, 5, 8, 8, 8, 13, 8, 21, 8
DELTATAB_E  DB      -34, 13, -21, 13, -13, 13, -8, 13, -5, 13, -3, 13, -2, 13, -1, 13
            DB      0, 13, 1, 13, 2, 13, 3, 13, 5, 13, 8, 13, 13, 13, 21, 13
DELTATAB_F  DB      -34, 21, -21, 21, -13, 21, -8, 21, -5, 21, -3, 21, -2, 21, -1, 21
            DB      0, 21, 1, 21, 2, 21, 3, 21, 5, 21, 8, 21, 13, 21, 21, 21

            END
